# -*- coding: utf-8 -*-
# &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
# D7Cl0ud360
# 邮箱: 18656042877@163.com
# 作者：'sugar'
# 公司网址： http://www.dtcloud360.com/
# Copyright 中亿丰信息科技(苏州)有限公司
# 日期：2023/7/31 14:17
# &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
import base64

from abc import ABC, abstractmethod
from datetime import datetime, date
from werkzeug.datastructures import FileStorage
from dtcloud.models import Model
from dtcloud.fields import Field, Command

converts = {}


class Convert(ABC):
    field_type: str

    @classmethod
    def __init_subclass__(cls):
        super().__init_subclass__()
        converts[cls.field_type] = cls

    @classmethod
    @abstractmethod
    def to_read(cls, rec: Model, field: Field, **kwargs):
        pass

    @classmethod
    @abstractmethod
    def to_field(cls, value):
        pass


class CharConvert(Convert):
    field_type = 'char'

    @classmethod
    def to_read(cls, rec, field, **kwargs):
        return rec[field.name]

    @classmethod
    def to_field(cls, value):
        return str(value) if value else ''


class IntegerConvert(CharConvert):
    field_type = 'integer'

    @classmethod
    def to_field(cls, value):
        return int(value) if value else 0


class FloatConvert(CharConvert):
    field_type = 'float'

    @classmethod
    def to_field(cls, value):
        return round(float(value), 2) if value else 0.00


class TextConvert(CharConvert):
    field_type = 'text'


class HtmlConvert(CharConvert):
    field_type = 'html'


class SelectionConvert(CharConvert):
    field_type = 'selection'

    @classmethod
    def to_read(cls, rec, field, **kwargs):
        if callable(field.selection):
            field_selection = field.selection(rec)
        elif isinstance(field.selection, str):
            field_selection = getattr(rec, field.selection)()
        else:
            field_selection = field.selection

        return dict(field_selection).get(rec[field.name])

    @classmethod
    def to_field(cls, value):
        return str(value) if value is not None else ''


class Many2OneConvert(Convert):
    field_type = "many2one"

    @classmethod
    def to_read(cls, rec, field, **kwargs):
        one_type = field.type
        if one_type == 'binary':
            value = {
                'id': rec[field.name].id,
                'name': '/public/image/%s/%s/%s' % (rec[field.name], rec[field.name].id, 'name') if rec['name'] else ''
            }
        else:
            name = rec[field.name]._rec_name
            value = {
                'id': rec[field.name].id or '',
                'name': getattr(rec[field.name], name) if isinstance(getattr(rec[field.name], name), str) else '',
            }

        return value

    @classmethod
    def to_field(cls, value):
        return int(value) if value else None


class One2ManyConvert(Convert):
    field_type = 'one2many'

    @classmethod
    def to_read(cls, rec, field, **kwargs):
        record = rec[field.name]
        if not record:
            return []

        fields = ('id', record._rec_name) if record._rec_name else None
        return record.read(fields)

    @classmethod
    def to_field(cls, value):
        if isinstance(value, str):
            data_rec = value.split(',') if ',' in value else [value]
        else:
            data_rec = value
        if isinstance(data_rec, tuple) or isinstance(data_rec, list):
            return [Command.link(int(d)) for d in data_rec]

        return [Command.link(int(data_rec))]


class Many2ManyConvert(One2ManyConvert):
    field_type = 'many2many'

    @classmethod
    def to_field(cls, value):
        if isinstance(value, str):
            data_rec = value.split(',') if ',' in value else [value]
        elif isinstance(value, int):
            data_rec = [value]
        else:
            data_rec = value

        if isinstance(data_rec, tuple) or isinstance(data_rec, list):
            return [Command.set([int(d) for d in data_rec])]

        raise ValueError(f'{value} value error')


class BoolConvert(Convert):
    field_type = 'boolean'

    @classmethod
    def to_read(cls, rec, field, **kwargs):
        return rec[field.name] in ('true', 'True', 1, '1', 'on', True)

    @classmethod
    def to_field(cls, value):
        return value in ('true', 'True', 1, '1', 'on', True)


class BinaryConvert(Convert):
    field_type = 'binary'

    @classmethod
    def to_read(cls, rec, field, **kwargs):
        if isinstance(rec[field.name], bytes):
            value = rec[field.name].decode()
        else:
            value = rec[field.name] or ''

        return value

    @classmethod
    def to_field(cls, value):
        if value:
            if isinstance(value, FileStorage):
                return base64.b64encode(value.read())

            return b'' if value in ['undefined', 'null'] else value

        return b''


class DateConvert(Convert):
    field_type = 'date'
    date_format = "%Y-%m-%d"

    @classmethod
    def to_read(cls, rec, field, **kwargs):
        return rec[field.name].strftime(cls.date_format) if rec[field.name] else ""

    @classmethod
    def to_field(cls, value):
        if not value:
            return None
        if isinstance(value, str):
            return datetime.strptime(value, cls.date_format).date()
        elif isinstance(value, datetime):
            return value.date()


class DatetimeConvert(Convert):
    field_type = 'datetime'
    date_format = "%Y-%m-%d %H:%M:%S"

    @classmethod
    def to_read(cls, rec, field, **kwargs):
        return rec[field.name].strftime(cls.date_format) if rec[field.name] else ""

    @classmethod
    def to_field(cls, value):
        if not value:
            return None
        if isinstance(value, str):
            return datetime.strptime(value, cls.date_format)
        elif isinstance(value, datetime):
            return value
